home *** CD-ROM | disk | FTP | other *** search
- /* Vestigial AX.25 link layer, understands only UI frames */
-
- #include <stdio.h>
- #include "machdep.h"
- #include "mbuf.h"
- #include "iface.h"
- #include "timer.h"
- #include "arp.h"
- #include "slip.h"
- #include "ax25.h"
- #include <ctype.h>
-
- #ifdef TRACE
- #include "trace.h"
- #endif
-
- /* AX.25 broadcast address: "QST-0" in shifted ascii */
- struct ax25_addr ax25_bdcst = {
- 'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1,
- ('0'<<1) | E,
- };
- struct ax25_addr mycall;
- int digipeat; /* Controls digipeating */
-
- /* Send IP datagrams in AX.25 UI frames using ARP */
- int
- ax_send(bp,interface,gateway,precedence,delay,throughput,reliability)
- struct mbuf *bp;
- struct interface *interface;
- int32 gateway;
- char precedence;
- char delay;
- char throughput;
- char reliability;
- {
- char *hw_addr,*res_arp();
-
- hw_addr = res_arp(interface,ARP_AX25,gateway,bp);
- if(hw_addr != NULLCHAR)
- (*interface->output)(interface,(struct ax25_addr *)hw_addr,
- interface->hwaddr,PID_IP,bp);
- }
- /* Send a raw packet to the KISS TNC using AX.25 link header.
- * Note that the calling order here must match ec_output
- * since ARP also uses it.
- */
- kiss_output(interface,dest,src,pid,bp)
- struct interface *interface;
- struct ax25_addr *dest; /* Destination AX.25 address (7 bytes, shifted) */
- struct ax25_addr *src; /* Source AX.25 address (7 bytes, shifted) */
- char pid; /* Protocol ID */
- struct mbuf *bp; /* Data field (follows PID) */
- {
- register struct mbuf *hbp;
- struct mbuf *ax_encode();
- struct slip *sp;
-
- if((bp = ax_encode(dest,src,pid,bp)) == NULLBUF)
- return;
- #ifdef TRACE
- if(trace & TRACE_AX25){
- printf("%s sent:\r\n",interface->name);
- if((trace & TRACE_HDR) > 1)
- ax25_dump(bp);
- if(trace & TRACE_DUMP)
- hexdump(bp);
- if(trace & TRACE_ASCII)
- asciidump(bp);
- fflush(stdout);
- }
- #endif
- /* Put type field for KISS TNC on front */
- if((hbp = alloc_mbuf(1)) == NULLBUF){
- free_p(bp);
- return;
- }
- *hbp->data = 0;
- hbp->cnt = 1;
- hbp->next = bp;
- slipq(interface->dev,hbp);
- }
-
- /* Put a AX.25 header on the front of a packet */
- struct mbuf *
- ax_encode(dest,src,pid,bp)
- struct ax25_addr *dest; /* Destination AX.25 address (7 bytes, shifted) */
- struct ax25_addr *src; /* Source AX.25 address (7 bytes, shifted) */
- char pid; /* Protocol ID */
- struct mbuf *bp; /* Data field (follows PID) */
- {
- register struct ax25_addr *ap;
- struct mbuf *abp;
- char *cp;
- int ndigi;
- int16 hdr_len;
-
- /* Determine length of dest addr */
- for(ndigi = 0,ap = dest; (ap->ssid & E) == 0;
- ap = (struct ax25_addr *) ((char *)ap + AXALEN))
- ndigi++;
-
- /* Compute header length:
- * 2 AX.25 address fields for source and dest +
- * "ndigi" AX.25 address field(s) for digipeaters +
- * 2 bytes for control and PID fields
- */
-
- hdr_len = (2 + ndigi)*AXALEN + 2;
-
- /* Create AX.25 link level header */
- if((abp = alloc_mbuf(hdr_len)) == NULLBUF){
- free_p(bp);
- return NULLBUF;
- }
- abp->cnt = hdr_len;
-
- /* Now fill it in */
-
- ap = (struct ax25_addr *)(abp->data);
-
- bcopy((char *)dest,(char *)ap,AXALEN);
- ap->ssid &= ~E;
- ap = (struct ax25_addr *) ((char *)ap + AXALEN);
- dest = (struct ax25_addr *) ((char *)dest + AXALEN);
-
- bcopy((char *)src,(char *)ap,AXALEN);
- ap->ssid &= ~E;
- cp = (char *)ap; /* get pointer to last address (source) */
- ap = (struct ax25_addr *) ((char *)ap + AXALEN);
-
- while(ndigi-- != 0) {
- bcopy((char *)dest,(char *)ap,AXALEN);
- dest = (struct ax25_addr *) ((char *)dest + AXALEN);
- cp = (char *)ap;
- ap = (struct ax25_addr *) ((char *)ap + AXALEN);
- }
- ((struct ax25_addr *)cp)->ssid |= E; /* Mark end of address field */
-
- cp = (char *)ap; /* Point to first byte past address field */
- *cp++ = UI;
- *cp++ = pid;
-
- abp->next = bp; /* Link in data field */
- return abp;
- }
- /* Process incoming KISS TNC frame */
- kiss_recv(interface,bp)
- struct interface *interface;
- struct mbuf *bp;
- {
- pullup(&bp,NULLCHAR,1); /* remove KISS TNC type field */
- if(bp != NULLBUF)
- ax_recv(interface,bp);
- }
- /* Process incoming AX.25 packets.
- * After optional tracing, the address field is examined. If it is
- * directed to us as a digipeater, repeat it. If it is addressed to
- * us or to QST-0, kick it upstairs depending on the protocol ID.
- */
- int
- ax_recv(interface,bp)
- struct interface *interface;
- struct mbuf *bp;
- {
- void arp_input(),ip_route();
- struct ax25_addr *ap,*ap1;
- char pid,multicast,ours,*control,*cbyte();
- int addrsize;
- struct mbuf *hbp;
-
- #ifdef TRACE
- if(trace & TRACE_AX25){
- printf("%s recv:\r\n",interface->name);
- if((trace & TRACE_HDR) > 1)
- ax25_dump(bp);
- if(trace & TRACE_DUMP)
- hexdump(bp);
- if(trace & TRACE_ASCII)
- asciidump(bp);
- fflush(stdout);
- }
- #endif
- control = cbyte(bp); /* control -> control byte */
-
- ap = (struct ax25_addr *)bp->data; /* -> address field */
- addrsize = control - (char *)ap; /* # bytes in address field */
- /* Check for either a missing control byte or a residual length
- * address field
- */
- if(control == NULL || addrsize % AXALEN != 0){
- free_p(bp);
- return;
- }
- addrsize /= AXALEN; /* # addresses in address field */
- /* Check for invalid address field (too short or odd length) */
- if(addrsize < 2) {
- free_p(bp);
- return;
- }
- /* Rescan, looking for our call in the repeater fields, if any.
- * Repeat appropriate packets.
- */
- /* for(ap1 = &ap[2]; ap1 < &ap[addrsize]; ap1++){ */
-
- for(ap1 = (struct ax25_addr *) ((char *)ap + 2*AXALEN);
- ap1 < (struct ax25_addr *) ((char *)ap + addrsize*AXALEN);
- ap1 = (struct ax25_addr *) ((char *)ap1 + AXALEN)) {
-
- if((ap1->ssid & REPEATED) == 0){
- /* Check if packet is directed to us as a digipeater */
- if(digipeat && addreq(ap1,&mycall)){
- /* Yes, kick it back out */
- ap1->ssid |= REPEATED;
- /* Put type field for KISS TNC on front */
- if((hbp = alloc_mbuf(1)) == NULLBUF){
- free_p(bp);
- return;
- }
- *hbp->data = 0;
- hbp->cnt = 1;
- hbp->next = bp;
- slipq(interface->dev,hbp);
- } else {
- /* Addressed to some other digipeater */
- free_p(bp);
- }
- return;
- }
- }
- /* Packet has passed all repeaters, now look at destination */
- ours = 0;
- if(addreq(&ap[0],&ax25_bdcst)){
- multicast = 1; /* Broadcast packet */
- } else if(addreq(&ap[0],&mycall)){
- multicast = 0; /* Packet directed at us */
- ours = 1;
- #if 0
- /* we really do want to see all of the packets, later on */
- } else {
- /* Not for us */
- free_p(bp);
- return;
- #endif
- }
- /* Now remove the header and the control field. Note: This will
- * have to be changed if the connected mode of AX.25 (ugh!) is ever
- * implemented
- */
- pullup(&bp,NULLCHAR,1 + addrsize * AXALEN);
-
- /* Examine the protocol ID field and switch to the right protocol */
- if(pullup(&bp,&pid,1) != 1)
- return; /* No PID, probably not an I-frame */
- switch(pid & 0xff){
- case PID_ARP:
- arp_input(interface,bp);
- break;
- case PID_IP:
- if (!(ours || multicast)) {
- free_p(bp);
- break;
- }
- ip_route(bp,multicast);
- break;
- #ifdef NETROM
- case PID_NETROM:
- netrom_input(interface, bp);
- break;
- #endif
- default:
- free_p(bp);
- break;
- }
- }
- /* Display or change our AX.25 address */
- domycall(argc,argv)
- int argc;
- char *argv[];
- {
- char buf[15];
-
- if(argc < 2){
- pax25(buf,&mycall);
- printf("%s\r\n",buf);
- return 0;
- }
- if(setcall(&mycall,argv[1]) == -1)
- return -1;
- mycall.ssid |= E;
- }
- /*
- * setcall - convert callsign plus substation ID of the form
- * "KA9Q-0" to AX.25 (shifted) address format
- * Address extension bit is left clear
- * Return -1 on error, 0 if OK
- */
- int
- setcall(out,call)
- struct ax25_addr *out;
- char *call;
- {
- int csize;
- unsigned ssid;
- register int i;
- register char *cp,*dp;
- char c,*index();
-
- if(out == (struct ax25_addr *)NULL || call == NULLCHAR || *call == '\0'){
- return -1;
- }
- /* Find dash, if any, separating callsign from ssid
- * Then compute length of callsign field and make sure
- * it isn't excessive
- */
- dp = index(call,'-');
- if(dp == NULLCHAR)
- csize = strlen(call);
- else
- csize = dp - call;
- if(csize > 6)
- return -1;
- /* Now find and convert ssid, if any */
- if(dp != NULLCHAR){
- dp++; /* skip dash */
- ssid = atoi(dp);
- if(ssid > 15)
- return -1;
- } else
- ssid = 0;
- /* Copy upper-case callsign, left shifted one bit */
- cp = out->call;
- for(i=0;i<csize;i++){
- c = *call++;
- if(islower(c))
- c = toupper(c);
- *cp++ = c << 1;
- }
- /* Pad with shifted spaces if necessary */
- for(;i<6;i++)
- *cp++ = ' ' << 1;
-
- /* Insert substation ID field and set reserved bits */
- out->ssid = 0x60 | (ssid << 1);
- return 0;
- }
- static
- addreq(a,b)
- register struct ax25_addr *a,*b;
- {
- if(bcmp(a->call,b->call,ALEN) != 0)
- return 0;
- if((a->ssid & SSID) != (b->ssid & SSID))
- return 0;
- return 1;
- }
- /* Convert encoded AX.25 address to printable string */
- pax25(e,addr)
- char *e;
- struct ax25_addr *addr;
- {
- register int i;
- char c,*cp;
-
- cp = addr->call;
- for(i=6;i != 0;i--){
- c = (*cp++ >> 1) & 0x7f;
- if(c == ' ')
- break;
- *e++ = c;
- }
- sprintf(e,"-%d",(addr->ssid >> 1) & 0xf); /* ssid */
- }
- /* Print a string of AX.25 addresses in the form
- * "KA9Q-0 [via N4HY-0,N2DSY-2]"
- */
- psax25(e,addr)
- register char *e;
- register struct ax25_addr *addr;
- {
- int i;
-
- for(i=0;;i++){
- pax25(e,addr);
- if(addr->ssid & E)
- break;
- if(i == 0)
- strcat(e," via ");
- else
- strcat(e,",");
- e += strlen(e);
- /* addr++; */
- addr = (struct ax25_addr *) ((char *)addr + AXALEN);
- }
- }
- /* Return a pointer to the control byte in the given frame */
- char *
- cbyte(fp)
- register struct mbuf *fp;
- {
- register char *cp;
- register unsigned cnt;
-
- if(fp == NULLBUF || fp->data == NULLCHAR)
- return NULLCHAR;
-
- cnt = fp->cnt;
- cp = fp->data;
- while(cnt != 0 && (*cp & E) == 0){
- cnt--;
- cp++;
- }
- /* If the address field never ended, cnt = 0; if it ended
- * on the last byte of a frame, cnt = 1. In either case,
- * there is no control field
- */
- if(cnt <= 1)
- return NULLCHAR;
- else
- return cp + 1;
- }
- dokiss(argc,argv)
- int argc;
- char *argv[];
- {
- struct interface *ifp;
- struct mbuf *hbp;
- int i;
- char *cp;
-
- if(argc < 2){
- printf("Interface name missing\r\n");
- return 1;
- }
- for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){
- if(strcmp(argv[1],ifp->name) == 0)
- break;
- }
- if(ifp == NULL){
- printf("Interface \"%s\" unknown\r\n",argv[1]);
- return 1;
- }
- if(ifp->output != kiss_output){
- printf("Interface \"%s\" not kiss\r\n",argv[1]);
- return 1;
- }
- if(argc < 3){
- printf("Data field missing\r\n");
- return 1;
- }
- /* Number of bytes in message == number of args - 2, since
- * first two args are "kiss" and the interface name
- */
- if((hbp = alloc_mbuf(argc - 2)) == NULLBUF){
- free_p(hbp);
- return;
- }
- hbp->cnt = argc - 2;
- hbp->next = NULL;
- for(i=2,cp = hbp->data;i < argc;i++,cp++){
- *cp = htoi(argv[i]);
- }
- slipq(ifp->dev,hbp);
- }
- #ifdef TRACE
-
- /* Dump an AX.25 packet header */
- ax25_dump(abp)
- struct mbuf *abp;
- {
- struct mbuf *bp;
- struct ax25_addr src,dest,addr;
- char tmp[20],*cp,control,cmdrsp,pid;
- int16 len,type,ftype();
-
- dup_p(&bp,abp,0,len_mbuf(abp));
-
- /* Read and print the destination and source addresses */
- if(pullup(&bp,(char *)&dest,AXALEN) != AXALEN)
- goto quit;
- if(pullup(&bp,(char *)&src,AXALEN) != AXALEN)
- goto quit;
-
- pax25(tmp,&src);
- printf("AX25: %s",tmp);
- pax25(tmp,&dest);
- printf("->%s",tmp);
-
- if((src.ssid & E) == 0){
- /* Find the last address entry in the digi string */
- printf(" v");
- while(pullup(&bp,(char *)&addr,AXALEN) == AXALEN){
- pax25(tmp,&addr);
- #ifdef AMIGA
- /* this is just personal preference; reminds me of
- the WA8DED TNC-1 code */
- printf(" %s%s",tmp,(addr.ssid & REPEATED) ? "*":"");
- #else
- printf(" %s%s",tmp,(addr.ssid & REPEATED) ? "H":"");
- #endif
- if(addr.ssid & E)
- break; /* Found it */
- }
- }
- if(pullup(&bp,&control,1) != 1)
- goto quit;
-
- putchar(' ');
- type = ftype(control);
- switch(type){
- case I:
- printf("I");
- break;
- case SABM:
- printf("SABM");
- break;
- case DISC:
- printf("DISC");
- break;
- case DM:
- printf("DM");
- break;
- case UA:
- printf("UA");
- break;
- case RR:
- printf("RR");
- break;
- case RNR:
- printf("RNR");
- break;
- case REJ:
- printf("REJ");
- break;
- case FRMR:
- printf("FRMR");
- break;
- case UI:
- printf("UI");
- break;
- default:
- printf("[invalid]");
- }
-
- if((dest.ssid & C) != (src.ssid & C)){
- if(dest.ssid & C)
- cmdrsp = COMMAND;
- else
- cmdrsp = RESPONSE;
- } else
- cmdrsp = UNKNOWN;
- /* Dump poll/final bit */
- if(control & PF){
- switch(cmdrsp){
- case COMMAND:
- printf("(P)");
- break;
- case RESPONSE:
- printf("(F)");
- break;
- default:
- printf("(P/F)");
- break;
- }
- }
- /* Dump sequence numbers */
- if((type & 0x3) != U) /* I or S frame? */
- printf(" NR=%d",(control>>5)&7);
- if((type & 0x1) == I) /* I frame? */
- printf(" NS=%d",(control>>1)&7);
-
- if(pullup(&bp,&pid,1) != 1)
- goto quit;
- printf(" pid 0x%x\r\n",pid & 0xff);
- if((trace & TRACE_HDR) > 2){
- switch(pid & 0xff){
- case PID_ARP:
- arp_dump(bp);
- break;
- case PID_IP:
- ip_dump(bp);
- break;
- #ifdef NETROM
- case PID_NETROM:
- netrom_dump(bp);
- break;
- #endif
- }
- }
- free_p(bp);
- fflush(stdout);
- return;
- quit: /* I hate go-to's, but sometimes there's no real choice */
- free_p(bp);
- printf("\r\n");
- fflush(stdout);
- }
- /* Figure out the frame type from the control field
- * This is done by masking out any sequence numbers and the
- * poll/final bit after determining the general class (I/S/U) of the frame
- */
- static
- int16
- ftype(control)
- register char control;
- {
- if((control & 1) == 0) /* An I-frame is an I-frame... */
- return I;
- if(control & 2) /* U-frames use all except P/F bit for type */
- return(control & ~PF);
- else /* S-frames use low order 4 bits for type */
- return(control & 0xf);
- }
- #endif
-